use crate::controller::Error;
use crate::service::ServiceSet;
use crate::{dto, mq};
use std::sync::Arc;

#[derive(Copy, Clone)]
enum EventType {
    Setup,
    Delete,
}

pub struct EventHandler {
    service_set: Arc<ServiceSet>,
    mq_client_set: Arc<mq::client::ClientSet>,
}

impl EventHandler {
    pub fn new(service_set: Arc<ServiceSet>, mq_client_set: Arc<mq::client::ClientSet>) -> Self {
        Self {
            service_set,
            mq_client_set,
        }
    }

    pub fn spawn_handle_setup_event(&self, candidate_path_id: &str) {
        self.spawn_handle_event(EventType::Setup, candidate_path_id)
    }

    pub fn spawn_handle_delete_event(&self, candidate_path_id: &str) {
        self.spawn_handle_event(EventType::Delete, candidate_path_id)
    }

    fn spawn_handle_event(&self, event_type: EventType, candidate_path_id: &str) {
        let candidate_path_id = candidate_path_id.to_string();
        let handler = Self::new(self.service_set.clone(), self.mq_client_set.clone());
        tokio::spawn(async move {
            if let Err(e) = handler.handle_event(event_type, candidate_path_id.as_str()).await {
                log::error!("{}", e);
            }
        });
    }

    async fn handle_event(&self, event_type: EventType, candidate_path_id: &str) -> Result<(), Error> {
        let candidate_path = self
            .service_set
            .candidate_path_manager
            .get(candidate_path_id)
            .await?;

        let te_policy_group = self
            .service_set
            .te_policy_group
            .get(candidate_path.inner.te_policy_group_id.as_str())
            .await?;

        let limit = wanmesh_common::dto::LIMIT_MAX;
        let mut offset = 0;
        loop {
            let traffic_steering_rule_list = self
                .service_set
                .traffic_steering_rule
                .list(
                    &dto::core::traffic_steering_rule::Filter {
                        te_policy_group_id: Some(te_policy_group.te_policy_group_id.clone()),
                        ..Default::default()
                    },
                    offset,
                    limit,
                )
                .await?;

            if traffic_steering_rule_list.is_empty() {
                break;
            }

            self.handle_event_by_traffic_steering_rules(
                event_type,
                candidate_path_id,
                &traffic_steering_rule_list,
            )
                .await?;

            if traffic_steering_rule_list.len() < limit as usize {
                break;
            }

            offset = offset + limit;
        }

        Ok(())
    }

    async fn handle_event_by_traffic_steering_rules(
        &self,
        event_type: EventType,
        candidate_path_id: &str,
        traffic_steering_rule_list: &[dto::core::traffic_steering_rule::TrafficSteeringRuleItem],
    ) -> Result<(), Error> {
        for traffic_steering_rule in traffic_steering_rule_list {
            self.handle_event_by_traffic_steering_rule(
                event_type,
                candidate_path_id,
                traffic_steering_rule,
            )
                .await?;
        }

        Ok(())
    }

    async fn handle_event_by_traffic_steering_rule(
        &self,
        event_type: EventType,
        candidate_path_id: &str,
        traffic_steering_rule: &dto::core::traffic_steering_rule::TrafficSteeringRuleItem,
    ) -> Result<(), Error> {
        let endpoint = self
            .service_set
            .endpoint_manager
            .get(traffic_steering_rule.inner.endpoint_id.as_str())
            .await?;

        match event_type {
            EventType::Setup => {
                self
                    .mq_client_set
                    .node
                    .endpoint
                    .traffic_steering_rule
                    .te_policy_group
                    .candidate_path
                    .publish_setup_event(
                        endpoint.inner.node_id.as_str(),
                        dto::event::endpoint::traffic_steering_rule::te_policy_group::candidate_path::SetupEventPayload {
                            endpoint_id: traffic_steering_rule.inner.endpoint_id.clone(),
                            traffic_steering_rule_id: traffic_steering_rule
                                .traffic_steering_rule_id
                                .clone(),
                            te_policy_group_id: traffic_steering_rule.traffic_steering_rule_id.clone(),
                            candidate_path_id: candidate_path_id.to_string(),
                        },
                    )
                    .await?;
            }
            EventType::Delete => {
                self
                    .mq_client_set
                    .node
                    .endpoint
                    .traffic_steering_rule
                    .te_policy_group
                    .candidate_path
                    .publish_delete_event(
                        endpoint.inner.node_id.as_str(),
                        dto::event::endpoint::traffic_steering_rule::te_policy_group::candidate_path::DeleteEventPayload {
                            endpoint_id: traffic_steering_rule.inner.endpoint_id.clone(),
                            traffic_steering_rule_id: traffic_steering_rule
                                .traffic_steering_rule_id
                                .clone(),
                            te_policy_group_id: traffic_steering_rule.traffic_steering_rule_id.clone(),
                            candidate_path_id: candidate_path_id.to_string(),
                        },
                    ).await?;
            }
        }


        Ok(())
    }
}
